/* eslint-disable no-bitwise, no-mixed-operators, class-methods-use-this */
// const { BigInteger } = require("jsbn");
// const SM3Digest = require("./sm3");
// const _ = require("./utils")
import { BigInteger } from "jsbn";;
import {SM3Digest} from './sm3'
import _ from './utils'
export class SM2Cipher {
	constructor() {
		this.ct = 1;
		this.p2 = null;
		this.sm3keybase = null;
		this.sm3c3 = null;
		this.key = new Array(32);
		this.keyOff = 0;
	}

	reset() {
		this.sm3keybase = new SM3Digest();
		this.sm3c3 = new SM3Digest();
		const xWords = _.hexToArray(_.leftPad(this.p2.getX().toBigInteger().toRadix(16), 64));
		const yWords = _.hexToArray(_.leftPad(this.p2.getY().toBigInteger().toRadix(16), 64));
		this.sm3keybase.blockUpdate(xWords, 0, xWords.length);
		this.sm3c3.blockUpdate(xWords, 0, xWords.length);
		this.sm3keybase.blockUpdate(yWords, 0, yWords.length);
		this.ct = 1;
		this.nextKey();
	}

	nextKey() {
		const sm3keycur = new SM3Digest(this.sm3keybase);
		sm3keycur.update((this.ct >> 24) & 0x00ff);
		sm3keycur.update((this.ct >> 16) & 0x00ff);
		sm3keycur.update((this.ct >> 8) & 0x00ff);
		sm3keycur.update(this.ct & 0x00ff);
		sm3keycur.doFinal(this.key, 0);
		this.keyOff = 0;
		this.ct++;
	}

	initEncipher(userKey) {
		const keypair = _.generateKeyPairHex();
		const k = new BigInteger(keypair.privateKey, 16);
		let publicKey = keypair.publicKey;

		this.p2 = userKey.multiply(k); // [k](Pb)
		this.reset();

		if (publicKey.length > 128) {
			publicKey = publicKey.substr(publicKey.length - 128);
		}

		return publicKey;
	}

	encryptBlock(data) {
		this.sm3c3.blockUpdate(data, 0, data.length);
		for (let i = 0; i < data.length; i++) {
			if (this.keyOff === this.key.length) {
				this.nextKey();
			}
			data[i] ^= this.key[this.keyOff++] & 0xff;
		}
	}

	initDecipher(userD, c1) {
		this.p2 = c1.multiply(userD);
		this.reset();
	}

	decryptBlock(data) {
		for (let i = 0; i < data.length; i++) {
			if (this.keyOff === this.key.length) {
				this.nextKey();
			}
			data[i] ^= this.key[this.keyOff++] & 0xff;
		}
		this.sm3c3.blockUpdate(data, 0, data.length);
	}

	doFinal(c3) {
		const yWords = _.hexToArray(_.leftPad(this.p2.getY().toBigInteger().toRadix(16), 64));
		this.sm3c3.blockUpdate(yWords, 0, yWords.length);
		this.sm3c3.doFinal(c3, 0);
		this.reset();
	}

	createPoint(x, y) {
		const publicKey = "04" + x + y;
		const point = _.getGlobalCurve().decodePointHex(publicKey);
		return point;
	}
}
// export default {SM2Cipher}
// module.exports = SM2Cipher;
